#include "libcomm.h"
#include "./sqlite/sqlite3.h"

#define TABLE_ROW(table,columnCount,rowIndex)	((const char**)(table+(rowIndex+1)*columnCount))
#define TABLE_ROW_COLUMN(table,columnCount,rowIndex,columnIndex)	((const char*)(TABLE_ROW(table,columnCount,rowIndex)[columnIndex]))
#define TABLE_COLUMN_NAME(table,columnIndex)	((const char*)table[columnIndex])

SqlStatement::SqlStatement(const char* sql, void* db, int* rc)
	:m_db(db),m_stmt(NULL),m_rc(rc)
{
	if(SQLITE_OK != (*m_rc=sqlite3_prepare_v2((sqlite3*)m_db, sql, -1, (sqlite3_stmt**)&m_stmt, 0))) 
	{
		LOGWARN("prepare sql \'%s\' failure: %s.",sql,sqlite3_errmsg((sqlite3*)m_db));
		m_stmt = NULL;
		return;
	}
}

SqlStatement::~SqlStatement()
{
	if(NULL != m_stmt)
	{
		sqlite3_finalize((sqlite3_stmt*)m_stmt);
		m_stmt = NULL;
	}
}

bool SqlStatement::IsValid()
{
	return (NULL != m_stmt);
}

SqlStatement::DataType SqlStatement::GetDataType(int index)
{
	return DataType(sqlite3_column_type((sqlite3_stmt*)m_stmt, index));
}

int SqlStatement::ValueInt (int index)
{
	return sqlite3_column_int((sqlite3_stmt*)m_stmt, index);
}

double SqlStatement::ValueFloat(int index)
{
	return sqlite3_column_double((sqlite3_stmt*)m_stmt, index);
}

String SqlStatement::ValueString(int index)
{
	return (char*)sqlite3_column_text((sqlite3_stmt*)m_stmt, index);
}

Buffer SqlStatement::ValueBlob(int index)
{
	Buffer buffer;
	const void* value = sqlite3_column_blob((sqlite3_stmt*)m_stmt, index);
	int size = sqlite3_column_bytes((sqlite3_stmt*)m_stmt, index);
	buffer.resize(size);
	memcpy(buffer.get(),value,size);
	return buffer;
}

/*
bool SqlStatement::Bind(int index, const char* value, bool copy)
{
	if (SQLITE_OK != (*m_rc=sqlite3_bind_text(
		(sqlite3_stmt*)m_stmt,
		index + 1, 
		value,
		strlen(value)+1, 
		copy?SQLITE_TRANSIENT:SQLITE_STATIC
		))) 
	{
		LOGWARN("bind sql text parameter failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}
*/

bool SqlStatement::Bind(int index, const void* value, int size, bool copy)
{
	if (SQLITE_OK != (*m_rc=sqlite3_bind_blob(
		(sqlite3_stmt*)m_stmt,
		index + 1, 
		value,
		size, 
		copy?SQLITE_TRANSIENT:SQLITE_STATIC
		))) 
	{
		LOGWARN("bind sql blob parameter failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}

bool SqlStatement::Bind(int index, double value)
{
	if (SQLITE_OK != (*m_rc=sqlite3_bind_double(
		(sqlite3_stmt*)m_stmt,
		index + 1, 
		value
		))) 
	{
		LOGWARN("bind sql double parameter failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}

bool SqlStatement::Bind(int index, int value)
{
	if (SQLITE_OK != (*m_rc=sqlite3_bind_int(
		(sqlite3_stmt*)m_stmt,
		index + 1, 
		value
		))) 
	{
		LOGWARN("bind sql int parameter failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}

bool SqlStatement::BindNull(int index)
{
	if (SQLITE_OK != (*m_rc=sqlite3_bind_null(
		(sqlite3_stmt*)m_stmt,
		index + 1
		))) 
	{
		LOGWARN("bind sql null parameter failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}

bool SqlStatement::Execute()
{
	*m_rc = sqlite3_step((sqlite3_stmt*)m_stmt);
	if (*m_rc == SQLITE_BUSY) 
	{
		LOGWARN("execute sql failure: SQLITE_BUSY"); 
		return false;
	}
	if (*m_rc == SQLITE_ERROR) 
	{
		LOGWARN("execute sql failure: SQLITE_ERROR; %s;",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	if (*m_rc == SQLITE_MISUSE) 
	{
		LOGWARN("execute sql failure: SQLITE_MISUSE");
		return false;
	}
	if(*m_rc != SQLITE_ROW && *m_rc != SQLITE_DONE)
	{
		LOGWARN("execute sql failure: %d; %s;",*m_rc,sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}

	Reset();
	return true;
}

bool SqlStatement::NextRow()
{
	*m_rc = sqlite3_step((sqlite3_stmt*)m_stmt);
	if (*m_rc == SQLITE_BUSY) 
	{
		LOGWARN("execute sql failure: SQLITE_BUSY"); 
		return false;
	}
	if (*m_rc == SQLITE_ERROR) 
	{
		LOGWARN("execute sql failure: SQLITE_ERROR; %s;",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	if (*m_rc == SQLITE_MISUSE) 
	{
		LOGWARN("execute sql failure: SQLITE_MISUSE");
		return false;
	}
	if(*m_rc != SQLITE_ROW && *m_rc != SQLITE_DONE)
	{
		LOGWARN("execute sql failure: %d; %s;",*m_rc,sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}

	if (*m_rc == SQLITE_DONE) 
	{
		Reset();
		return false;
	}
	return true;
}

bool SqlStatement::Reset(bool clear_bindings)
{
	int rc;
	if(SQLITE_OK != (rc=sqlite3_reset((sqlite3_stmt*)m_stmt)))
	{
		LOGWARN("reset sql statement failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	if(clear_bindings && SQLITE_OK != (rc=sqlite3_clear_bindings((sqlite3_stmt*)m_stmt)))
	{
		LOGWARN("clear sql statement bindings failure: %s.",sqlite3_errmsg((sqlite3*)m_db));
		return false;
	}
	return true;
}

////////////////////////////////////////////////////////////////////////// SqlDatabase::DataTable

SqlDatabase::DataTable::DataTable()
:pazResult(NULL),nRow(0),nColumn(0)
{
}

SqlDatabase::DataTable::~DataTable()
{
	if(NULL != pazResult)
	{
		sqlite3_free_table(pazResult);
		pazResult = NULL;
	}
}

bool SqlDatabase::DataTable::IsValid() const
{
	return (NULL!=pazResult);
}

int SqlDatabase::DataTable::Rows() const
{
	return nRow;
}

int SqlDatabase::DataTable::Columns() const
{
	return nColumn;
}

const char** SqlDatabase::DataTable::Row(int index) const
{
	MYLIB_ASSERT(NULL!=pazResult && index>=0 && index<nRow && nColumn>0);
	return TABLE_ROW(pazResult,nColumn,index);
}

const char** SqlDatabase::DataTable::ColumnNames() const
{
	MYLIB_ASSERT(NULL!=pazResult);
	return (const char**)pazResult;
}

const char** SqlDatabase::DataTable::operator[](int row) const
{
	return Row(row);
}

const char* SqlDatabase::DataTable::Cell(int row,int column) const
{
	MYLIB_ASSERT(column>=0 && column<nColumn);
	return Row(row)[column];
}

////////////////////////////////////////////////////////////////////////// SqlDatabase

SqlDatabase::SqlDatabase():m_db(NULL),m_rc(SQLITE_OK)
{
}

SqlDatabase::~SqlDatabase()
{
	Close();
}

bool SqlDatabase::Open(const char* conn)
{
	m_rc = sqlite3_open(conn, (sqlite3**)&m_db);
	if (SQLITE_OK != m_rc) 
	{
		LOGWARN("open sql database \'%s\' failure.",conn);
		m_db = NULL;
		return false;
	}
#ifdef __GNUC__
	if(-1 == chmod(conn,0666))
		LOGWARN("change permissions of database \'%s\' failure: %s.",conn,strerror(errno));
#endif
	return true;
}

void SqlDatabase::Close()
{
	if(NULL != m_db)
	{
		sqlite3_close((sqlite3*)m_db);
		m_db = NULL;
	}
}

bool SqlDatabase::Select(const char* sql, DataTable& dt)
{
	char **pazResult = NULL;
	int nRow;
	int nColumn;
	char *errmsg = NULL;

	m_rc = sqlite3_get_table((sqlite3*)m_db,sql,&pazResult,&nRow,&nColumn,&errmsg);
	if(SQLITE_OK != m_rc)
	{
		LOGWARN("execute sql \'%s\' failure: %s;",sql,errmsg);
		if(NULL != errmsg)
			sqlite3_free(errmsg);;
		if(NULL != pazResult)
			sqlite3_free_table(pazResult);
		return false;
	}

	dt.pazResult = pazResult;
	dt.nRow = nRow;
	dt.nColumn = nColumn;
	return true;
}

bool SqlDatabase::Execute(const char* sql)
{
	bool ret = false;
	SqlStatement* stmt = this->Prepare(sql);
	if(stmt != NULL)
	{
		ret = stmt->Execute();
		delete stmt;
	}
	return ret;
}

bool SqlDatabase::ExecuteBatch(const char* sql_batch)
{
	char *errmsg = NULL;
	m_rc = sqlite3_exec((sqlite3*)m_db,sql_batch,NULL,NULL,&errmsg);
	if(SQLITE_OK != m_rc)
	{
		LOGWARN("execute batch sql \'%s\' failure: %s;",sql_batch,errmsg);
		goto SQL_EXECUTEBATCH_FAILURE__;
	}

SQL_EXECUTEBATCH_FAILURE__:
	if(NULL != errmsg)
		sqlite3_free(errmsg);
	return (SQLITE_OK==m_rc);
}

SqlStatement* SqlDatabase::Prepare(const char* sql)
{
	SqlStatement* stmt = new SqlStatement(sql,m_db,&m_rc);
	MYLIB_ASSERT_NOTNULL(stmt);
	if(!stmt->IsValid())
	{
		delete stmt;
		stmt = NULL;
	}
	return stmt;
}

bool SqlDatabase::Begin()
{
	return Execute("begin");
}

bool SqlDatabase::Commit()
{
	return Execute("commit");
}

bool SqlDatabase::Rollback()
{
	return Execute("rollback");
}